package com.sheepone.util.structure.list;

import com.sheepone.util.structure.IndexedContainer;
import com.sheepone.util.structure.node.BidirectionalNode;
import com.sheepone.util.structure.node.UnidirectionalNode;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * @author Miss.杨
 * @description 双向链表
 * 1. 列表为空 head=last=null
 * 2. size=1 head、node[0]、last
 * 3. size=2 head、node[0]、node[1]、last
 * @since 2024/5/3 - 20:45
 */
public class BidirectionalList<T> extends IndexedContainer implements DataNodeList<T,BidirectionalNode<T>> {
    private BidirectionalNode<T> head;
    private BidirectionalNode<T> tail;

    public BidirectionalList() {
        this(Integer.MAX_VALUE);
    }

    public BidirectionalList(int capacity) {
        super(capacity);
        this.head = new BidirectionalNode<>();
        this.tail = new BidirectionalNode<>();
        head.setNext(tail);
        tail.setPrev(head);
    }

    @Override
    public boolean addNode(BidirectionalNode<T> node) {
        if (node == null || isFull()) {
            return false;
        }
        // 尾插法
        UnidirectionalNode<T> prev = tail.getPrev();
        prev.setNext(node);
        node.setPrev(prev);
        node.setNext(tail);
        tail.setPrev(node);
        return true;
    }

    @Override
    public void addNode(int index, BidirectionalNode<T> node) {
        checkIndex(index);
        if (isFull()) {
            return;
        }
        BidirectionalNode<T> prev = getNode(index);
        node.setPrev(prev);
        node.setNext(prev.getNext());
        prev.setNext(node);
        ((BidirectionalNode<T>) prev.getNext()).setPrev(node);
    }

    @Override
    public void addAllNode(Collection<? extends BidirectionalNode<T>> c) {
        if (!hasSpace(c.size())) {
            return;
        }
        c.forEach(this::addNode);
    }

    @Override
    public void addAllNode(int index, Collection<? extends BidirectionalNode<T>> c) {
        checkIndex(index);
        if (c.isEmpty() || !hasSpace(c.size())) {
            return;
        }
        BidirectionalNode<T> prev = getNode(index);
        for (BidirectionalNode<T> node : c) {
            node.setPrev(prev);
            node.setNext(prev.getNext());
            prev.setNext(node);
            ((BidirectionalNode<T>) prev.getNext()).setPrev(node);
            prev = node;
        }
    }

    @Override
    public boolean containsNode(BidirectionalNode<T> o) {
        if (isEmpty()) {
            return false;
        }
        BidirectionalNode<T> node = (BidirectionalNode<T>) head.getNext();
        while (node != tail) {
            if (node == o) {
                return true;
            }
            node = (BidirectionalNode<T>) node.getNext();
        }
        return false;
    }

    @Override
    @SuppressWarnings("unchecked")
    public boolean containsAllNode(Collection<?> c) {
        for (Object o : c) {
            try {
                if (!containsNode((BidirectionalNode<T>) o)) {
                    return false;
                }
            } catch (Exception e) {
                return false;
            }
        }
        return true;
    }

    @Override
    public BidirectionalNode<T> getNode(int index) {
        checkIndex(index);
        BidirectionalNode<T> node = (BidirectionalNode<T>) head.getNext();
        int no = 0;
        while (node != tail) {
            if (no++ == index) {
                return node;
            }
            node = (BidirectionalNode<T>) node.getNext();
        }
        return null;
    }

    @Override
    public int indexOfNode(BidirectionalNode<T> o) {
        if (isEmpty()) {
            return -1;
        }
        UnidirectionalNode<T> node = head.getNext();
        for (int i = 0; i < size; i++) {
            if (node == o) {
                return i;
            }
            node = node.getNext();
        }
        return -1;
    }

    @Override
    public Iterator<BidirectionalNode<T>> nodeIterator() {
        return null;
    }

    @Override
    public boolean removeNode(BidirectionalNode<T> o) {
        if (isEmpty()) {
            return false;
        }
        BidirectionalNode<T> node = (BidirectionalNode<T>) head.getNext();
        for (int i = 0; i < size; i++) {
            if (node == o) {
                node.getPrev().setNext(node.getNext());
                ((BidirectionalNode<T>) node.getNext()).setPrev(node.getPrev());
                size--;
                return true;
            }
            node = (BidirectionalNode<T>) node.getNext();
        }
        return false;
    }

    @Override
    public BidirectionalNode<T> removeNode(int index) {
        BidirectionalNode<T> node = getNode(index);
        node.getPrev().setNext(node.getNext());
        ((BidirectionalNode<T>) node.getNext()).setPrev(node.getPrev());
        node.setNext(null);
        node.setPrev(null);
        size--;
        return node;
    }

    @Override
    public boolean add(T t) {
        return addNode(new BidirectionalNode<>(t));
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        if (c.isEmpty() || !hasSpace(c.size())) {
            return false;
        }
        c.forEach(this::add);
        return true;
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        if (c.isEmpty() || !hasSpace(c.size())) {
            return false;
        }
        List<BidirectionalNode<T>> list = c.stream().map(data -> new BidirectionalNode<T>(data)).toList();
        addAllNode(index, list);
        return true;
    }


    @Override
    public boolean contains(Object o) {
        if (isEmpty()) {
            return false;
        }
        BidirectionalNode<T> node = (BidirectionalNode<T>) head.getNext();
        while (node != tail) {
            if (node.getValue().equals(o)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean containsAll(Collection<T> c) {
        for (Object o : c) {
            try {
                if (!contains(o)) {
                    return false;
                }
            } catch (Exception e) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean remove(Object o) {
        BidirectionalNode<T> node = (BidirectionalNode<T>) head.getNext();
        while (node != tail) {
            if (node.getValue().equals(o)) {
                node.getPrev().setNext(node.getNext());
                ((BidirectionalNode<T>) node.getNext()).setPrev(node.getPrev());
                size--;
                return true;
            }
            node = (BidirectionalNode<T>) node.getNext();
        }
        return false;
    }

    @Override
    public boolean removeAll(Collection<T> c) {
        for (Object object : c) {
            if (!remove(object)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void clear() {
        head.setNext(tail);
        tail.setPrev(head);
        size = 0;
    }

    @Override
    public T get(int index) {
        checkIndex(index);
        return getNode(index).getValue();
    }

    @Override
    public T set(int index, T element) {
        checkIndex(index);
        BidirectionalNode<T> node = getNode(index);
        T data = node.getValue();
        node.setValue(element);
        return data;
    }

    @Override
    public void add(int index, T element) {
        if (isFull()) {
            return;
        }
        addNode(index, new BidirectionalNode<>(element));
    }

    @Override
    public T remove(int index) {
        return removeNode(index).getValue();
    }

    @Override
    public int indexOf(Object o) {
        if (isEmpty()) {
            return -1;
        }
        BidirectionalNode<T> node = (BidirectionalNode<T>) head.getNext();
        for (int i = 0; i < size; i++) {
            if (node.getValue().equals(o)) {
                return i;
            }
            node = (BidirectionalNode<T>) node.getNext();
        }
        return -1;
    }

    @Override
    public int lastIndexOf(Object o) {
        if (isEmpty()) {
            return -1;
        }
        BidirectionalNode<T> node = (BidirectionalNode<T>) tail.getPrev();
        for (int i = size - 1; i >= 0; i--) {
            if (node.getValue().equals(o)) {
                return i;
            }
            node = (BidirectionalNode<T>) node.getPrev();
        }
        return -1;
    }
}
